package stendcontrol;

import gui.usergui.ResultFrame;
import java.awt.Color;
import java.io.Serializable;
import java.util.ArrayList;
import javax.swing.SwingUtilities;

public class Experiment
        implements MessageListener, Serializable {

    /**
     * Associations
     */
    private transient ArrayList<AbstractMessage> messages = new ArrayList<AbstractMessage>();
    private MeasureSeqMessage first_measure_seq = null;
    private CoefConfigurator coef_conf = CoefConfigurator.getConf();
    private String exp_name;
    private static transient int n_exp = 0;

    /**
     *
     * @param coef_conf - конфигуратор коэффициента пропорциональности.
     */
    public Experiment() {
        this("noname_exp" + (++n_exp));
    }

    public Experiment(String name) {
        for (int i = 0; i < Configurator.COUNT_SENSOR; i++) {
            //cached_process[i] = false;
            data_sensors[i] = new DataSensor("Сенсор №" + (i + 1));
        }
        data_sensors[Configurator.COUNT_SENSOR] = new DataSensor("Результаты");
        this.exp_name = name;
    }
    /*private boolean[] cached_process = new boolean[Configurator.COUNT_SENSOR];
     private double[][] cache_process;
     private boolean cached_position = false;
     private double[] cache_position;
     private Func[] cache_process_func = new Func[Configurator.COUNT_SENSOR];
     private Func cache_position_func;*/
    private boolean saved = false;
    /**
     * true - Эксперимент закрыт для редактирования
     */
    private boolean closed = true;
    /**
     * true - Эксперимент обработан
     */
    private boolean processed = false;
    private int expEngine = 0;
    private DataSensor[] data_sensors =
            new DataSensor[Configurator.COUNT_SENSOR + 1];
    ArrayList<Boolean> result_next_graph = new ArrayList<Boolean>();
    ArrayList<Color> colors_graph = new ArrayList<Color>();
    /*private double[][] korr_func;
     private double[] samp_korr_func;
     private double[][] spect_func;
     private double[] samp_spect_func;*/
    /*private double[] max_value;OR];
     /*private double[][] korr_func;
     private double[] samp_korr_func;
     private double[][] spect_func;
     private double[] min_value;
     private double[] mean;
     private double[] disp;*/

    /**
     * Стандартный метод установки поля
     *
     * @param exp_name
     */
    public void setExpName(String exp_name) {
        this.exp_name = exp_name;
    }

    /**
     * Стандартный метод получения содержимого поля
     *
     * @return
     */
    public String getExpName() {
        return exp_name;
    }

    /**
     *
     * Возвращает n-ое сообщение
     *
     * @param n
     * @return AbstractMessage
     */
    public AbstractMessage getMessage(int n) {
        if (n >= 0 && n < messages.size()) {
            return messages.get(n);
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Bad index for getMessage"
                    + " in Experement. Bad index:" + n + " Min "
                    + "Index:0 Max Index:" + messages.size());
            return null;
        }
    }

    /**
     * Возвращает первую последовательность измерений.
     *
     * @return MeasureSeqMessage
     */
    public MeasureSeqMessage getFirstMeasureSeq() {
        return first_measure_seq;
    }

    /**
     * Operation Возвращает данные с датчиков
     *
     * @return double []
     */
    public double[] getProcess(int nsensor) {
        if (closed) {
            if (nsensor >= 0 && nsensor < Configurator.COUNT_SENSOR) {
                if (first_measure_seq == null) {
                    return new double[0];
                } else {
                    int coef_0 = coef_conf.getSensorCoef0(nsensor);
                    double coef_prop = coef_conf.getSensorCoefProp(nsensor);
                    int cnt_mes = getCountMeasures();
                    double[] proc = new double[cnt_mes];
                    int proc_i = 0;
                    MeasureSeqMessage msm = first_measure_seq;
                    do {
                        for (int j = 0; j < msm.getSize(); j++) {
                            proc[proc_i++] = (msm.getSensorValue(nsensor, j)
                                    - coef_0) * coef_prop;
                        }
                    } while ((msm = msm.getNextMeasureSeq()) != null);

                    /*boolean created_buff_proc = false;
                     for (int j = 0; j < Configurator.COUNT_SENSOR; j++) {
                     created_buff_proc |= cached_process[j];
                     }
                     if (!created_buff_proc) {
                     cache_process = new double[Configurator.COUNT_SENSOR][cnt_mes];
                     }
                     System.arraycopy(proc, 0, cache_process[nsensor], 0,
                     cnt_mes);
                     cache_process_func[nsensor] =
                     new Func("Измерения", Func.F_PROCESS,
                     cache_process[nsensor], 1);
                     data_sensors[nsensor].removeAllFunc(Func.F_PROCESS);
                     data_sensors[nsensor].addFunc(cache_process_func[nsensor]);
                     cached_process[nsensor] = true;*/

                    return proc;
                }
            } else {
                return new double[0];
            }
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "getProcess unavailable for "
                    + "open Experement");
            return new double[0];
        }
    }

    /**
     * Вовращает позицию измерения (точка измерения)
     *
     * @return double[]
     */
    public double[] getPositions() {
        /*cache_position = new double[cnt_mes];
         * System.arraycopy(pos, 0, cache_position, 0, cnt_mes);
         * cache_position_func =
         * new Func("Позиция измерений", Func.F_POSITIONS,
         * cache_position, 1);
         * for (DataSensor ds : data_sensors) {
         * ds.removeAllFunc(Func.F_POSITIONS);
         * ds.addFunc(cache_position_func);
         * }
         * cached_position = true;*/
        if (closed) {
            if (first_measure_seq == null) {
                return new double[0];
            } else {

                int coef_0 = coef_conf.getPositionCoef0(expEngine);
                double coef_prop = coef_conf.getPositionCoefProp(expEngine);
                int cnt_mes = getCountMeasures();
                double[] pos = new double[cnt_mes];
                int pos_i = 0;
                MeasureSeqMessage msm = first_measure_seq;
                do {
                    for (int j = 0; j < msm.getSize(); j++) {
                        pos[pos_i++] = (msm.getPosition(j)
                                - coef_0) * coef_prop;
                    }
                } while ((msm = msm.getNextMeasureSeq()) != null);


                return pos;
            }
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "getPosition unavailable"
                    + " for open Experement");
            return new double[0];
        }
    }

    public void updateDataSensors() {
        double pos[] = this.getPositions();
        for (int i = 0; i < Configurator.COUNT_SENSOR; i++) {
            data_sensors[i].removeAllFunc(Func.F_PROCESS);
            data_sensors[i].addFunc(new Func("Измерения", Func.F_PROCESS,
                    this.getProcess(i), pos));
        }
    }

    public Func getProcessFunc(int nsensor) {
        if (nsensor >= 0 && nsensor < Configurator.COUNT_SENSOR) {
            return data_sensors[nsensor].getFuncByType(Func.F_PROCESS);
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Bad index:" + nsensor + " for "
                    + "getProccessFunc in Experiment");
            return null;
        }
    }

    /*public Func getPositionFunc() {
     if (!cached_position) {
     getPositions();
     }
     return cache_position_func;
     }*/
    /**
     * Метка символизирующая о том, что эксперимент подвергся обработке(подсчет
     * спектра, фильтрация, апроксимативный анализ)
     *
     * @return boolean
     */
    public boolean isProcessed() {
        return processed;
    }

    /**
     * Метка символизирующая о закрытии эксперимента
     *
     * @return
     */
    public boolean isClosed() {
        return closed;
    }

    /**
     * Добавляет сообщение при условии, что эксперимент закончился.
     *
     * @param msg
     */
    @Override
    public void msgBuilded(AbstractMessage msg) {

        //System.out.println("Experiment class, msgBuilded");
        if (!closed) {
            messages.add(msg);
            /*if (msg.getTypeMsg() == AbstractMessage.MSG_MSEQ) {
             System.out.println("MSEQ: " +((MeasureSeqMessage) msg).getSize()  );
             }
             else if (msg.getTypeMsg() == AbstractMessage.MSG_STS) {
                
             System.out.println("STS: " +((StatusMessage) msg).getParam());
             }*/
            if (first_measure_seq == null) {
                if (msg.getTypeMsg() == AbstractMessage.MSG_MSEQ) {
                    first_measure_seq = (MeasureSeqMessage) msg;
                }
            }
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Adding to closed"
                    + " Experiment");
        }
    }

    /**
     * Подсчет количества измерений в эксперименте.
     *
     * @return int
     */
    public int getCountMeasures() {
        if (first_measure_seq == null) {
            return 0;
        } else {
            int len = 0;
            MeasureSeqMessage msm = first_measure_seq;
            do {
                len += msm.getSize();
            } while ((msm = msm.getNextMeasureSeq()) != null);
            return len;
        }
    }

    /**
     * Ничего пока не делает.
     */
    public void clear() {
    }

    public void clearCache() {
        //cache_position = new double[0];
        //cached_position = false;
       /*for (int i = 0; i < Configurator.COUNT_SENSOR; i++) {
         //cache_process[i] = new double[0];
         data_sensors[i].removeAllFunc();
         data_sensors[i].removeAllParam();
         //cached_process[i]=false;
         }*/
    }

    /**
     * Принудительное открытие эксперимента.
     */
    public void open() {
        closed = false;
    }

    /**
     * Принудительное закрытие эксперимента.
     */
    public void close() {
        closed = true;
    }

    /**
     * Метка обработки эксперимента.
     */
    public void processed() {
        processed = true;
    }

    public void addFunc(Func fc, int nsensor) {
        if (nsensor >= 0 && nsensor < Configurator.COUNT_SENSOR) {
            if (data_sensors[nsensor].containTypeFunc(fc.getType())
                    && fc.getType() != Func.F_OTHER) {
                StendLogger.log(StendLogger.MSG_WARNING, "Sensor already "
                        + "contain this type(" + fc.getType() + ") of Func");
            }
            data_sensors[nsensor].addFunc(fc);
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Bad index:" + nsensor
                    + " for addFunc in Experiment. Can't add Function");
        }
    }

    public void addParam(Param pr, int nsensor) {
        if (nsensor >= 0 && nsensor < Configurator.COUNT_SENSOR) {
            if (data_sensors[nsensor].containTypeParam(pr.getType())
                    && pr.getType() != Param.P_OTHER) {
                StendLogger.log(StendLogger.MSG_WARNING, "Sensor already "
                        + "contain this type(" + pr.getType() + ") of Param");
            }
            data_sensors[nsensor].addParam(pr);
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Bad index:" + nsensor
                    + " for addParam in Experiment. Can't add Function");
        }
    }

    public void setFunc(Func fc, int nsensor) {
        if (nsensor >= 0 && nsensor < Configurator.COUNT_SENSOR) {
            if (fc.getType() != Func.F_OTHER) {
                data_sensors[nsensor].removeAllFunc(fc.getType());
            }
            data_sensors[nsensor].addFunc(fc);
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Bad index:" + nsensor
                    + " for setFunc in Experiment. Can't add Function");
        }
    }

    public void setParam(Param pr, int nsensor) {
        if (nsensor >= 0 && nsensor < Configurator.COUNT_SENSOR) {
            if (pr.getType() != Param.P_OTHER) {
                data_sensors[nsensor].removeAllParam(pr.getType());
            }
            data_sensors[nsensor].addParam(pr);
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Bad index:" + nsensor
                    + " for setParam in Experiment. Can't add Function");
        }
    }

    public DataSensor getDataSensor(int nsensor) {
        if (nsensor >= 0 && nsensor < Configurator.COUNT_SENSOR) {
            return data_sensors[nsensor];
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Bad index:" + nsensor
                    + " for getDataSensor in Experiment");
            return null;
        }
    }

    public Func getFunc(int nsensor, int num) {
        return getDataSensor(nsensor).getFunc(num);
    }

    public Func getFunc(int nsensor, int type, int num) {
        return getDataSensor(nsensor).getFuncByType(type, num);
    }

    public double getParam(int nsensor, int num) {
        return getDataSensor(nsensor).getParam(num).getValue();
    }

    public double getParam(int nsensor, int type, int num) {
        return getDataSensor(nsensor).getParam(num).getValue();
    }

    public void addResultFunc(Func res, boolean next_graph) {
        data_sensors[Configurator.COUNT_SENSOR].addFunc(res);
        result_next_graph.add(next_graph);
        colors_graph.add(null);
    }

    public void addResultFunc(Func res, boolean next_graph, Color color) {
        data_sensors[Configurator.COUNT_SENSOR].addFunc(res);
        result_next_graph.add(next_graph);
        colors_graph.add(color);
    }

    public void addResultFunc(Func res, boolean next_graph, int r, int g,
            int b) {
        data_sensors[Configurator.COUNT_SENSOR].addFunc(res);
        result_next_graph.add(next_graph);
        colors_graph.add(new Color(r, g, b));
    }

    public ArrayList<Color> getColorsGraph() {
        return colors_graph;
    }

    public void addResultParam(Param res) {
        data_sensors[Configurator.COUNT_SENSOR].addParam(res);
    }

    public DataSensor getDataResult() {
        return data_sensors[Configurator.COUNT_SENSOR];
    }

    public boolean isNextGraph(int n) {
        return result_next_graph.get(n);
    }

    /**
     * Возвращает имя эксперимента.
     *
     * @return String.
     */
    @Override
    public String toString() {
        return exp_name;
    }

    public int getIndexDataSensor(DataSensor ds) {
        for (int i = 0; i < Configurator.COUNT_SENSOR; i++) {
            if (data_sensors[i].equals(ds)) {
                return i;
            }
        }
        return -1;
    }

    public int getCountMessage() {
        return this.messages.size();

    }

    public void addMessage(AbstractMessage msg) {
        this.messages.add(msg);
    }

    public void setMessages(ArrayList<AbstractMessage> messages) {
        this.messages = messages;

    }

    public boolean isSaved() {
        return saved;
    }

    public void setSaved(boolean saved) {
        this.saved = saved;
    }

    public void setExpEngine(int expEngine) {
        this.expEngine = expEngine;
    }
    
    transient Experiment view_result_experiment = null;
    public void viewResult(){
        view_result_experiment = this;
        SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        new ResultFrame(view_result_experiment).setVisible(true);
                    }
                });
    }
}
